#include <stdio.h>
#include <map>
#include <string.h>
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <map>
#include <sys/stat.h>

#ifdef _WINDOWS
	#include "common.h"
	#include <Windows.h>
	#define ERROR_BUFFER_LENGTH 65535
#else
	#include <sys/time.h>
	#include <dlfcn.h>
#endif

#include "DynLib.h"

using namespace std;

DynLib::DynLib(const char *libraryName) {

	if (libraryName==NULL || strlen(libraryName)==0) {
		cerr << "NULL or empty library name passed to DynLib constructor" << endl;
		return;
	}

	struct stat stFileInfo;
	if (0 != stat(libraryName,&stFileInfo)) {
		cerr << "Invalid library name '" << libraryName << "' passed to DynLib constructor" << endl;
		return;
	}
        
#ifndef _WINDOWS
	//Building on Linux
	lib_handle = NULL;
	lib_handle = dlopen(libraryName, RTLD_LAZY | RTLD_LOCAL);
	if (NULL == lib_handle) {
		char *error = dlerror();
		cerr << "Failed to load dynamic library '" << libraryName << "' - dlerror() returned " << (error==NULL?"NULL":error) << endl;
	}
#else
	//Building on Windows
		lib_handle = NULL;
		wchar_t* libraryNameW = NULL;
		cerr << "Loading library " << libraryName << endl;
		lib_handle = LoadLibraryExA((LPCSTR) libraryName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
		if (NULL == lib_handle) {
			DWORD errorId = GetLastError();
			char error[ERROR_BUFFER_LENGTH];
			FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error, ERROR_BUFFER_LENGTH, NULL);
			cerr << "Failed to load dynamic library '" << libraryName << "' - GetLastError() returned '" << (error==NULL?"NULL":error) << "'" << endl;
		}
		else { 
			cerr <<"Loaded library successfully." << endl;
		}
#endif
}

DynLib::~DynLib() {
}

void *DynLib::get_function(const char *functionName) {

#ifdef DEBUG
	cerr << "get_function(" << functionName << ")" << endl;
#endif

		if (NULL == lib_handle) {
			cerr << "Library not loaded: ";
			return NULL;
		}

		void *func = functionMap[functionName];
#ifndef _WINDOWS
		//Building on Linux
		if (func==NULL) {
			func = dlsym(lib_handle, functionName);
			if (!func) {
				char *error = dlerror();
				cerr << "Failed to resolve function '" << functionName << "' - dlerror() returned " << (error==NULL?"NULL":error) << endl;
				return NULL;
			}
			functionMap[functionName] = func;
		}
#else
		//Building on Windows
		if (func==NULL) {
			func = GetProcAddress(lib_handle, functionName);
			if (!func) {
				DWORD errorId = GetLastError();
				char error[ERROR_BUFFER_LENGTH];
				FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error, ERROR_BUFFER_LENGTH, NULL);
				cerr << "Failed to resolve function '" << functionName << "' - GetLastError returned '" << (error==NULL?"NULL":error) << "'" << endl;
			}
		}
#endif
		return func;
}
